package com.example.my_helper;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import static androidx.core.app.ActivityCompat.startActivityForResult;

public class MyBluetoothClient {

    final static int ON_CONNECT = 0;
    final static int ON_MESSAGE = 1;
    final static int ON_SEND = 2;
    final static int ON_CLOSE = 3;
    final static int ON_ERROR = 4;
    final static int NOT_FOUND = 5;
    final static int NO_PERMISSION = 6;
    final static int CONFIG_ERROR = 7;
    final static int UNSUPPORT = 8;

    static final String SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
    static final String CHARATE_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";

    private static final String TAG = "MyBluetoothClient";

    public String host;
    public int port;
    public Socket socket;
    public InputStream inputStream;
    public OutputStream outputStream;

    public Context context;
    public Handler handler;

    public boolean isConnected = false;
    public int buf_len = 0;
    public int recv_len = 0;
    public byte[] buf = new byte[4096];
    private int recv_tick = 0;
    private boolean connect_lock = false;
    //Queue<Byte> recv_queue;
    public BluetoothDebuger bluetoothDebuger;

    public BluetoothDevice bluetoothDevice;
    public BluetoothGatt bluetoothGatt;
    public BluetoothGattService bluetoothGattService;
    public BluetoothGattCharacteristic bluetoothGattCharacteristic;

    public MyBluetoothClient(Context context, Handler handler)
    {
        this.context = context;
        this.handler = handler;
        //this.recv_queue = new LinkedList<>();
    }


    public void Connect(BluetoothDevice bluetoothDevice)
    {
//        if(connect_lock)
//            return;
//        connect_lock = true;
        this.bluetoothDevice = bluetoothDevice;
        bluetoothGatt = bluetoothDevice.connectGatt(context, true, new MyBluetoothGattCallback());
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                if(isConnected && socket != null) {
//                    isConnected = false;
//                    try {
//                        socket.close();
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                }
//
//                try {
//                    socket = new Socket(host, port);
//                    outputStream = socket.getOutputStream();
//                    inputStream = socket.getInputStream();
//                    isConnected = true;
//                    handler.sendEmptyMessage(ON_CONNECT);
//                    new recv_thread().start();
//                    connect_lock = false;
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    handler.sendEmptyMessage(ON_ERROR);
//                    connect_lock = false;
//                }
//            }
//        }).start();
    }

    class MyBluetoothGattCallback extends BluetoothGattCallback{
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            Log.i("onConnectionStateChange", "status="+status+", newstate="+newState);
            if (status != BluetoothGatt.GATT_SUCCESS) {
                String err = "Cannot connect device with error status: " + status;
                // 当尝试连接失败的时候调用 disconnect 方法是不会引起这个方法回调的，所以这里
                //   直接回调就可以了。
                isConnected = false;
                gatt.close();
                bluetoothGatt = null;
                handler.sendEmptyMessage(ON_CLOSE);
                return;
            }
            if(newState == BluetoothGatt.STATE_CONNECTED)
            {
                isConnected = true;
                handler.sendEmptyMessage(ON_CONNECT);
                bluetoothGatt.requestMtu(131);
            }
            else if(newState == BluetoothGatt.STATE_DISCONNECTED)
            {
                isConnected = false;
                gatt.disconnect();
                gatt.close();
                bluetoothGatt = null;
                handler.sendEmptyMessage(ON_CLOSE);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            Log.i("onServicesDiscovered", "status="+status);
            int properties;
            try{
                bluetoothGattService = gatt.getService(UUID.fromString(SERVICE_UUID));
                List<BluetoothGattCharacteristic> bluetoothGattCharacteristics = bluetoothGattService.getCharacteristics();
                for(BluetoothGattCharacteristic bluetoothGattCharacteristic1 : bluetoothGattCharacteristics){
                    Log.i(TAG, "==bluetoothGattCharacteristic1.uuid="+bluetoothGattCharacteristic1.getUuid().toString());
                    properties = bluetoothGattCharacteristic1.getProperties();
                    Log.i(TAG, "properties="+properties);
                    if((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0)
                    {
                        Log.i(TAG, "(properties & BluetoothGattCharacteristic.PROPERTY_READ) != 0");
                        if(bluetoothGattCharacteristic == null)
                            bluetoothGattCharacteristic = bluetoothGattCharacteristic1;

                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                byte[] b = new byte[20];
                                b[0] = (byte) 0x82;
                                b[1] = 0x04;
                                b[2] = 0x00;
                                b[3] = 0x28;
//                                bluetoothGattCharacteristic.setValue(b);
//                                bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);

                                while(isConnected)
                                {
                                    bluetoothGattCharacteristic.setValue(b);
                                    bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
                                    try {
                                        Thread.sleep(55*1000);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }).start();

                        //Write(b, b.length);
                    }
                    if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                        Log.i(TAG, "==(properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0");
                        List<BluetoothGattDescriptor> bluetoothGattDescriptors = bluetoothGattCharacteristic1.getDescriptors();
                        for(BluetoothGattDescriptor bluetoothGattDescriptor : bluetoothGattDescriptors){
                            Log.i(TAG, "==bluetoothGattDescriptor.uuid="+bluetoothGattDescriptor.getUuid().toString());
                            bluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic1, true);
                            if (bluetoothGattDescriptor != null) {
                                Log.i(TAG, "==bluetoothGattDescriptor != null");
                                bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
                            }
                        }
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            Log.i("onCharacteristicRead", "status="+status+"characteristic="+characteristic.getStringValue(0));
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            Log.i("onCharacteristicWrite", "status="+status+"characteristic="+characteristic.getStringValue(0));
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.i("onCharacteristicChanged", "characteristic="+characteristic.getStringValue(0));
            Log.i("onCharacteristicChanged", "characteristic="+characteristic.getValue().length);
            bluetoothDebuger.Log_recv(characteristic.getValue(), characteristic.getValue().length);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            Log.i("onDescriptorRead", "status="+status+", descriptor="+ Arrays.toString(descriptor.getValue()));
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            Log.i("onDescriptorWrite", "status="+status+", descriptor="+ Arrays.toString(descriptor.getValue()));
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            Log.i("onMtuChanged", "status="+status+", mtu="+mtu);
            bluetoothGatt.discoverServices();
        }


    }


    /*public int Read(byte[] buf, int len, int timeout)
    {
        return Read(buf, 0, len, timeout);
    }


    public int Read(byte[] buf, int offset, int len, int timeout)
    {
        int i = 0;
        int size;
        while(true)
        {
            size = recv_queue.size();
            for(int t = 0; t < size; t++)
            {
                buf[i++] = recv_queue.poll();
                if(i == len)
                    return len;
            }
            if(timeout == 0 || !isConnected)
            {
                return i;
            }
            else if(timeout > 0)
            {
                timeout--;
            }
            try{
                Thread.sleep(1);
            }catch (Exception e)
            {
                e.printStackTrace();
            }

        }
    }

    public String Read(int timeout)
    {
        byte[] buf = new byte[1024];
        int len = Read(buf, 0, buf.length, timeout);
        if(len == 0)
            return null;
        else
            return new String(buf, 0, len);
    }

    public byte ReadByte()
    {
        while(true)
        {
            if(!recv_queue.isEmpty())
                return recv_queue.poll();
            if(isConnected)
                return -1;
        }
    }

    public String ReadLine()
    {
        byte[] buf = new byte[1024];
        int i = 0;
        while(true)
        {
            buf[i++] = ReadByte();
            if(i > buf.length || buf[i] == -1)
                return null;
            if(i > 0 && buf[i] == '\n' && buf[i-1] == '\r')
                return new String(buf, 0, i);
        }
    }*/

    public int Write(byte[] bytes, int len)
    {
        return Write(bytes,0 , len);
    }

    public int Write(byte[] bytes, int offset, int len)
    {
        if (!isConnected || bluetoothGattCharacteristic == null) {
            return -1;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
//                byte[] bytes1 = new byte[20];
//                int i;
//                for(i = 0; i < (len / 20); i++)
//                {
//                    System.arraycopy(bytes, offset+i*20, bytes1,0, 20);
//                    bluetoothGattCharacteristic.setValue(bytes1);
//                    bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
//                }
//                if(len % 20 != 0)
//                {
//                    bytes1 = new byte[len%20];
//                    System.arraycopy(bytes, offset+i*20, bytes1,0, len%20);
//                    bluetoothGattCharacteristic.setValue(bytes1);
//                    bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
//                }
                    //System.arraycopy(bytes, offset+i*20, bytes1,0, len%20);
                    bluetoothGattCharacteristic.setValue(Arrays.copyOfRange(bytes, offset, len));
                    bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
            }
        }).start();
        return 0;
    }

    public int Write(String string)
    {
        return Write(string.getBytes(), 0,  string.length());
    }

    public int WriteByte(byte b)
    {
        byte[] bytes = new byte[1];
        bytes[0] = b;
        return Write(bytes, 0,  1);
    }

    public int WriteLine(String string)
    {
        byte[] buf = (string+"\r\n").getBytes();
        return Write(buf, 0, buf.length);
    }

    /*public int Available(){
        return recv_queue.size();
    }*/

    public void Close()
    {
        if(isConnected)
        {
            try{
                if(bluetoothGatt != null)
                {
                    isConnected = false;
                    bluetoothGatt.disconnect();
                    bluetoothGatt.close();
                    bluetoothGatt = null;
                    handler.sendEmptyMessage(ON_CLOSE);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
//            isConnected = false;
//            handler.sendEmptyMessage(ON_CLOSE);
        }
    }

/*    private void put(byte b)
    {
        if (recv_queue.size() > buf_len) {
            recv_queue.poll();
        }
        if (!recv_queue.offer(b)) {
            recv_queue.poll();
            recv_queue.offer(b);
        }
    }*/

    class recv_thread extends Thread {
        public void run() {
            byte[] tmp = new byte[1024];
            int len;
            byte b;

            while (isConnected) {
                try {
                    b = (byte) inputStream.read();
                    if(b >= 0) {
                        tmp[buf_len++] = b;
                        while (true) {
                            len = inputStream.available();
                            if (len > 0) {
                                recv_tick = 0;
                                for (int i = 0; i < len; i++) {
                                    b = (byte) inputStream.read();
                                    if(b < 0)
                                        break;
                                    tmp[buf_len++] = b;
                                    if(buf_len >= 1024) {
                                        handler.removeMessages(ON_MESSAGE);
                                        bluetoothDebuger.Log_recv(tmp, buf_len);
                                        buf_len = 0;
                                    }
                                }
                            } else {
                                recv_tick++;
                                if(recv_tick > 20){
                                    handler.removeMessages(ON_MESSAGE);
                                    bluetoothDebuger.Log_recv(tmp, buf_len);
                                    buf_len = 0;
                                    break;
                                }
                                Thread.sleep(1);
                            }
                        }
                    } else {
                        Close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Close();
                }
            }
        }
    }
}
